package com.base.data.elasticsearch.util;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.base.data.elasticsearch.annotations.Field;
import com.base.data.elasticsearch.util.gson.JacksonDefaultDeserializerDate;
import com.base.data.elasticsearch.util.gson.JacksonDefaultSerializerDate;
import lombok.SneakyThrows;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import static com.base.data.elasticsearch.util.BaseDateUtil.TIME_PATTER_HHMMSS;
import static com.base.data.elasticsearch.util.BaseDateUtil.TIME_PATTER_YYYYMMDD;

/**
 * JsonDeserialize/JsonSerialize
 * 1.   @Transient 已被忽略
 * 2.   @Field 有注解时 Serialize 默认 有 Deserialize 是否有都不影响
 *          无注解时 Serialize 必须没有(防止有人没写注解，动态mapping) Deserialize 是否有都不影响
 *          [判定是ID列时 会序列化 有]
 * 3.   Date():
 *      非yyyy-MM-dd HH:mm:ss 格式情况下
 *          需序列化添加下面注解
 *          @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8")
 *          如需反序列化必须添加下面注解
 *          @JsonDeserialize(using = JacksonDefaultDeserializerDate.class)
 * 4.   LocalDateTime()/LocalDate()/LocalTime() 都是默认格式 如需其他格式添加序列化工具即可
 *
 * by magina
 */
public class JacksonUtil {

    protected static final ObjectMapper INSTANCE = new ObjectMapper();

    static {
        INSTANCE.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(BaseDateUtil.DEFAULT_TIME_PATTER)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(TIME_PATTER_YYYYMMDD)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_PATTER_HHMMSS)));
        javaTimeModule.addDeserializer(Date.class, new JacksonDefaultDeserializerDate());
        // 自定义 全局把时间转为 时间戳
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(BaseDateUtil.DEFAULT_TIME_PATTER)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(TIME_PATTER_YYYYMMDD)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_PATTER_HHMMSS)));
        javaTimeModule.addSerializer(Date.class, new JacksonDefaultSerializerDate());
        INSTANCE.registerModule(javaTimeModule);
        //skip annotations[]
        INSTANCE.setAnnotationIntrospector(AnnotationIntrospectorPair.pair(INSTANCE.getSerializationConfig().getAnnotationIntrospector(), new SkipAnnotationIntrospector()));
    }

    public static ObjectMapper create() {
        return INSTANCE;
    }

    @SneakyThrows
    public static String toString(Object o) {
        return create().writeValueAsString(o);
    }


    @SneakyThrows
    public static <T> T fromJson(String json, Class<T> clas) {
        return create().readValue(json, clas);
    }


}

class SkipAnnotationIntrospector extends NopAnnotationIntrospector {

    @Override
    public boolean hasIgnoreMarker(AnnotatedMember m) {
        return _isIgnorable(m);
    }

    protected boolean _isIgnorable(Annotated a) {
        if (a instanceof AnnotatedField) {
            if (_findAnnotation(a, javax.persistence.Transient.class) != null || _findAnnotation(a, Transient.class) != null) {
                return true;
            }
            if (null == _findAnnotation(a, Field.class)) {
                //特殊情况
                if ("java.lang.Integer#value".equals(((AnnotatedField) a).getFullName())) {
                    return false;
                }
                if (null != _findAnnotation(a, Id.class) || null != _findAnnotation(a, javax.persistence.Id.class) || "id" == a.getName()) {
                    return false;
                }
                return true;
            }
        }

        return false;
    }
}